package com.sl.diary.util;

import java.io.File;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import com.google.android.gms.maps.model.LatLngBounds;
import com.sl.diary.R;
import com.sl.diary.model.PointModel;

// TODO: Auto-generated Javadoc
/**
 * The Class Util.
 */
public class Util {
	/** The Constant TAG. */
	private static final String TAG = "Util";
	
	/**
	 * Parses the distance from string.
	 * 
	 * @param distance
	 *            the distance
	 * @return the int
	 */
	public static int parseDistanceFromString(String distance) {
		try {
			NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
			Number number = numberFormat.parse(distance);
			return number.intValue();
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
	}
	
	/**
	 * Checks if is integer.
	 *
	 * @param s the s
	 * @return true, if is integer
	 */
	public static boolean isInteger(String s) {
	    try { 
	        Integer.parseInt(s); 
	    } catch(NumberFormatException e) { 
	        return false; 
	    }
	    // only got here if we didn't return false
	    return true;
	}
	
	/**
	 * Check date.
	 *
	 * @param startDate the start date
	 * @param endDate the end date
	 * @return true, if successful
	 */
	public static boolean checkDate(Date startDate, Date endDate) {
		try {
			if(startDate.after(endDate) || startDate == null 
					|| endDate == null)
				return false;
			return true;
		} catch (Exception e) {
			// TODO: handle exception
			return false;
		}
	}
	
	/**
	 * Calculation by distance.
	 *
	 * @param startPointLat the start point lat
	 * @param startPointLong the start point long
	 * @param endPointLat the end point lat
	 * @param endPointLong the end point long
	 * @return the double
	 */
	public static int calculationByDistance(double startPointLat, double startPointLong,
			double endPointLat, double endPointLong) {
		
		int Radius = 6371 * 1000;// radius of earth in Km

		if(startPointLat < 0 || endPointLat < 0 || startPointLong < 0 || endPointLong < 0)
			return 0;
			
		double dLat = Math.toRadians(endPointLat - startPointLat);
		double dLon = Math.toRadians(endPointLong - startPointLong);
		double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
				+ Math.cos(Math.toRadians(startPointLat))
				* Math.cos(Math.toRadians(endPointLat)) * Math.sin(dLon / 2)
				* Math.sin(dLon / 2);
		double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
		double valueResult = Radius * c;
		return (int) Math.round(valueResult);
	}
	
	/**
	 * Calculate time hours.
	 * 
	 * @param startTime
	 *            the start time
	 * @param endTime
	 *            the end time
	 * @return the float
	 */
	public static long calculateTimeHours(Date startTime, Date endTime) {
		long minutes = calculateTimeMinutes(startTime, endTime);
		return minutes / 60;
	}

	/**
	 * Calculate time minutes.
	 * 
	 * @param startTime
	 *            the start time
	 * @param endTime
	 *            the end time
	 * @return the float
	 */
	public static long calculateTimeMinutes(Date startTime, Date endTime) {
		long seconds = calculateTimeSeconds(startTime, endTime);
		return seconds / 60;
	}

	/**
	 * Calculate time seconds.
	 * 
	 * @param startTime
	 *            the start time
	 * @param endTime
	 *            the end time
	 * @return the float
	 */
	public static long calculateTimeSeconds(Date startTime, Date endTime) {
		long millis = calculateTime(startTime, endTime);
		return millis / 1000;
	}

	/**
	 * Calculate time.
	 * 
	 * @param startTime
	 *            the start time
	 * @param endTime
	 *            the end time
	 * @return the float
	 */
	public static long calculateTime(Date startTime, Date endTime) {
		try {
			return Math.abs(endTime.getTime() - startTime.getTime());
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
	}
	
	/**
	 * Calculate velocity.
	 *
	 * @param startTime the start time
	 * @param distance the distance
	 * @return the velocity (km/h)
	 */
	public static float calculateVelocity(Date startTime, int distance){
		float timeDifference = calculateTimeSeconds(startTime, new Date());
		return (float) ((distance / timeDifference) * 3.6);
	}
	
	/**
	 * Parse date to string.
	 *
	 * @param date the date
	 * @return date string with format: YYYYMMDD
	 */
	public static String parseDateToString(Date date) {
		try {
			SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
			return df.format(date);
		} catch (Exception e) {
			// TODO: handle exception
			return "";
		}
	}
	
	/**
	 * Parse date to string.
	 *
	 * @param date the date
	 * @return date string with format: YYYYMMDD
	 */
	public static Date parseStringToDate(String date) {
		SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
		try {
			return df.parse(date);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			return null;
		}
	}
	
	/**
	 * Convert date to string by default format (dd/MM/yyyy).
	 *
	 * @param date the date
	 * @return the string
	 */
	public static String convertDateToString(Date date) {
		if (date != null) {
			SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
			return dateFormat.format(date);
		}
		return "";
	}
	
	/**
	 * Calculate nearest point.
	 *
	 * @param pointList the point list
	 * @param currentLocation the current location
	 * @return the array list
	 */
	public static ArrayList<PointModel> calculateNearestPoint(List<PointModel> pointList, 
												final Location currentLocation) {
		ArrayList<PointModel> points = new ArrayList<PointModel>();
		Collections.sort(pointList, new Comparator<PointModel>() {
				@Override
				  public int compare(PointModel point1, PointModel point2) {
					int distance1 = calculationByDistance(point1.getLatitude(), point1.getLongitude(),
							currentLocation.getLatitude(), currentLocation.getLongitude());
					
					int distance2 = calculationByDistance(point2.getLatitude(), point2.getLongitude(),
							currentLocation.getLatitude(), currentLocation.getLongitude());
					
				    return distance2 > distance1 ? -1 : (distance1 == distance2 ? 0 : 1);
				  }
			});
		
		points.addAll(pointList);
		return points;
	}
	
	public static ArrayList<PointModel> checkRegionBounds(ArrayList<PointModel> pointList, 
															LatLngBounds regionBounds) {
		ArrayList<PointModel> result = new ArrayList<PointModel>();
		
		double lowLat;
		double lowLng;
		double highLat;
		double highLng;

		if (regionBounds.northeast.latitude < regionBounds.southwest.latitude)
		{
			lowLat = regionBounds.northeast.latitude;
			highLat = regionBounds.southwest.latitude;
		}
		else
		{
			highLat = regionBounds.northeast.latitude;
			lowLat = regionBounds.southwest.latitude;
		}
		if (regionBounds.northeast.longitude < regionBounds.southwest.longitude)
		{
			lowLng = regionBounds.northeast.longitude;
			highLng = regionBounds.southwest.longitude;
		}
		else
		{
			highLng = regionBounds.northeast.longitude;
			lowLng = regionBounds.southwest.longitude;
		}
		
		for(PointModel point : pointList) {
			if(point.getLatitude() < lowLat || point.getLatitude() > highLat
					|| point.getLongitude() < lowLng || point.getLongitude() > highLng) 
				continue;
			result.add(point);
		}
		return pointList;
	}
	
	
	
	/**
	 * Checks if is network connected.
	 *
	 * @param context the context
	 * @return true, if is network connected
	 */
	public static boolean isNetworkConnected(Context context) {
		ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
	    NetworkInfo i = cm.getActiveNetworkInfo();
	    if (i == null){
	    	return false;
	    }else if (!i.isConnected()){
	    	return false;
	    }
	    else if (!i.isAvailable()){
	    	return false;
	    }
	    else{
	    	return true;
	    }
	}
	
	/**
	 * Scale and center crop image from file
	 * @param c image is gotten from resource to combine
	 * @param mCurrentPhotoPath image from file
	 * @return
	 */
	public static Bitmap scaleCenterCrop(Bitmap c, String mCurrentPhotoPath ) {
	    Bitmap source = BitmapFactory.decodeFile(mCurrentPhotoPath);
	    if(source == null)
	    	return null;
	    
		int sourceWidth = source.getWidth();
	    int sourceHeight = source.getHeight();

	    // Compute the scaling factors to fit the new height and width, respectively.
	    // To cover the final image, the final scaling will be the bigger 
	    // of these two.
	    int newWidth = c.getWidth() - Constant.horizontalMarkerImageMargin;
	    int newHeight = c.getHeight() - Constant.verticalMarkerImageMargin;
	    
	    float xScale = (float) newWidth / sourceWidth;
	    float yScale = (float) newHeight / sourceHeight;
	    float scale = Math.max(xScale, yScale);

	    // Now get the size of the source bitmap when scaled
	    float scaledWidth = scale * sourceWidth;
	    float scaledHeight = scale * sourceHeight;

	    // Let's find out the upper left coordinates if the scaled bitmap
	    // should be centered in the new size give by the parameters
	    float left = (newWidth - scaledWidth) / 2;
	    float top = (newHeight - scaledHeight) / 2;

	    // The target rectangle for the new, scaled version of the source bitmap will now
	    // be
	    RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);

	    // Finally, we create a new bitmap of the specified size and draw our new,
	    // scaled bitmap onto it.
	    Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
	    Canvas canvas = new Canvas(dest);
	    canvas.drawBitmap(source, null, targetRect, null);

	    Bitmap cs = null; 
		cs = Bitmap.createBitmap(c.getWidth(), c.getHeight(), Bitmap.Config.ARGB_8888);
	    Canvas comboImage = new Canvas(cs); 

		comboImage.drawBitmap(c, 0f, 0f, null); 
		comboImage.drawBitmap(dest, c.getWidth()/2 - dest.getWidth()/2 , 
				Constant.horizontalMarkerImageMargin/2, null); 
		dest.recycle();
		dest = null;
		System.gc();
		return cs; 
//	    return dest;
	}
	
	/**
	 * Get memory
	 * @param ctx
	 * @return
	 */
	public static long getMemory(Context ctx){
		MemoryInfo mi = new MemoryInfo();
		ActivityManager activityManager = (ActivityManager) ctx.getSystemService(ctx.ACTIVITY_SERVICE);
		activityManager.getMemoryInfo(mi);
		return mi.availMem / 1048576L;
	}
	
	/**
	 * Decode image marker.
	 *
	 * @param path the path
	 * @return the bitmap
	 */
	public static Bitmap decodeImageMarker(String path, Context ctx){
		DLog.e(TAG, "memory decodeImageMarker: " + getMemory(ctx));
		File file = new File(path);
		if(file.exists()) {
			Bitmap bm1 = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.icon_marker_03);
			return Util.scaleCenterCrop(bm1, path);
		} 
		return null;
	}
}
